home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Workbench Add-On
/
Workbench Add-On - Volume 1.iso
/
BBS-Archive
/
DiskUtil
/
Crunch
/
XFH.lha
/
XFH
/
SRC
/
XPK.C
< prev
Wrap
C/C++ Source or Header
|
1994-05-06
|
40KB
|
1,365 lines
/* xpk.c - routines for the master xpk.library.
Copyright (C) 1991, 1992, 1993 Kristian Nielsen.
This file is part of XFH, the compressing file system handler.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "CFS.h"
#include <dossupport.h>
#include <utility/hooks.h>
#include <utility/tagitem.h>
#include <libraries/xpk.h>
#include <string.h>
/* These two include files are changing all the time. Hence the #undef. */
#undef XpkFH
struct Library *XpkBase;
#ifndef min
#define min(a,b) ((a)>(b) ? (b) : (a))
#endif
/* 'Conditional' tag - passed only if condition true. */
#define TAGIF(c,t) ((c)?(t):TAG_IGNORE)
/* Structure for holding an xpk file unpacked in memory. */
struct unpackedxpk {
struct xpkmemchunk *first,*last,*current;
LONG currentpos;
};
/* Node in linked list of data buffers. */
struct xpkmemchunk {
struct xpkmemchunk *next;
LONG abspos;
LONG size;
UBYTE *data; /* MUST be alloced with dosalloc(). */
};
struct XpkFH {
struct CFSFH cfsfh;
struct unpackedxpk *unpackedxpk;
LONG filelen;
UBYTE *InBuf; /* Buffer last returned by inhook. */
UBYTE *OutBuf; /* Buffer last returned by outhook. */
LONG InBufLen; /* Of InBuf. (ToDo: Not really used...) */
LONG OutBufLen; /* Of OutBuf. (ToDo: Not really used...) */
};
struct XpkLock {
struct CFSLock cfslock;
};
static struct unpackedxpk *newunpackedxpk(void){
struct unpackedxpk *p;
if(!dalloc(p)) return NULL;
/* All fields 0 by default. */
return p;
}
/* NOTE: 'data' should be allocated with dosalloc().
* NOTE ALSO: Memory block may be larger than 'size' (will be freed by
* dosfree() ).
*/
static BOOL addxpkmemchunk(struct unpackedxpk *p, UBYTE *data, LONG size ){
struct xpkmemchunk *q;
if(!(dalloc(q))) return FALSE;
q->next=NULL;
q->size=size;
q->data=data;
if(!p->first){
q->abspos = 0L;
p->first=p->current=p->last = q;
}else{
q->abspos = p->last->abspos+p->last->size;
p->last->next = q;
p->last = q;
}
return TRUE;
}
static void killunpackedxpk(struct unpackedxpk *p){
struct xpkmemchunk *q,*r;
for(q=p->first;q;q=r){
r=q->next;
dfree(q->data);
dfree(q);
}
dfree(p);
}
static BOOL seekunpackedxpk(struct unpackedxpk *p, LONG abspos){
struct xpkmemchunk *q;
if(abspos < 0 || !p->first || p->last->abspos+p->last->size < abspos)
return (BOOL) (abspos == 0);
/* Special case: seek just beyond end. */
if(p->last->abspos+p->last->size == abspos){
debug(("Seeking to last position in file: %ld.\n",abspos));
p->current = p->last;
p->currentpos = abspos;
return DOSTRUE;
}
/* Start at current pos. if possible. */
q = p->current->abspos <= abspos ? p->current : p->first;
while(q->abspos+q->size <= abspos) q = q->next;
p->current = q;
p->currentpos = abspos;
return DOSTRUE;
}
static BOOL readunpackedxpk(struct unpackedxpk *p, UBYTE *buf, LONG size){
struct xpkmemchunk *q;
LONG len,chunkpos;
if(!p->first)
return (BOOL) (size==0);
q = p->current;
while(size>0){
if(p->last->abspos+p->last->size <= p->currentpos) return FALSE;
chunkpos=p->currentpos-q->abspos;
len = min(size,q->size-chunkpos);
CopyMem(q->data+chunkpos,buf,len);
size-=len;
buf+=len;
p->currentpos+=len;
if(p->currentpos >= q->abspos+q->size) q=q->next;
if(q) p->current = q;
}
return TRUE;
}
/*------------------------------------------------------------------------*
* Hooks for Xpk packing / unpacking.
*/
/* Structure for private hook data (glob & Xpkfilehandle). */
struct xpkhookdata {
glb glob;
struct XpkFH *fh;
};
/* Free Xpk input buffer if nessesary. */
static void FreeXpkInBuf(struct XpkFH *fh){
/* debug(("FreeXpkInBuf: Checking if buffers allocated...\n")); */
if(fh->InBuf){
/* debug(("FreeXpkInBuf: Freeing buffer: %lx,%ld.\n",fh->InBuf,fh->InBufLen)); */
dosfree(fh->InBuf);
fh->InBuf = NULL;
}
}
/* Allocate Xpk input buffer. */
static UBYTE *AllocXpkInBuf(struct XpkFH *fh, LONG size){
/* NOTE: I found out (the hard way...) that Xpk expects a buffer
* 4 bytes larger than requested ... wierd. */
size+=4;
FreeXpkInBuf(fh);
fh->InBufLen=size;
if(!(fh->InBuf=dosalloc(size))){
return 0L;
}
return fh->InBuf;
}
/* Free Xpk output buffer if nessesary. */
static void FreeXpkOutBuf(struct XpkFH *fh){
/* debug(("FreeXpkOutBuf: Checking if buffers allocated...\n")); */
if(fh->OutBuf){
/* debug(("FreeXpkOutBuf: Freeing buffer: %lx,%ld.\n",fh->OutBuf,fh->OutBufLen)); */
dosfree(fh->OutBuf);
fh->OutBuf = NULL;
}
}
/* Allocate Xpk output buffer. */
static UBYTE *AllocXpkOutBuf(struct XpkFH *fh, LONG size){
/* NOTE: I found out (the hard way...) that Xpk expects a buffer
* 4 bytes larger than requested ... wierd. */
size+=4;
FreeXpkOutBuf(fh);
fh->OutBufLen=size;
if(!(fh->OutBuf=dosalloc(size))){
return 0L;
}
return fh->OutBuf;
}
/* Transfer control to actual hook function. Shields the actual */
/* hook functions from any ugly assembler-like register considerations. */
static LONG __asm DoXpkHook( register __a0 struct Hook *hook,
register __a1 struct XpkIOMsg *msg,
register __a2 void *dummy){
struct xpkhookdata *hookdata = hook->h_Data;
LONG ret;
typedef LONG (*myhooktype)(glb, struct XpkFH *, ULONG, UBYTE **, LONG);
ret = (*(myhooktype)hook->h_SubEntry)
(hookdata->glob, hookdata->fh,msg->Type,(UBYTE **)&msg->Ptr,msg->Size);
if(ret) msg->IOError = hookdata->glob->ioerr;
return ret;
}
/* Calling conventions for hooks. */
/* ToDo: When docs become available, check against this description. */
/* */
/* XpkIOMsg->IOError is set only in case of non-zero hook return value. */
/* */
/* Big problem: What is XIO_SEEK supposed to return in XpkIOMsg->Ptr? */
/* The masterlib does the wierdest things... However, it (currently!) */
/* only needs a non-zero return in case of succes. */
/* */
/* XIO_GETBUF/XIO_WRITE in xpkunpackout(): It is assumed that only one */
/* buffer will be needed at any time, so that a second XIO_GETBUF may */
/* free the buffer returned by the first. It is also assumed that */
/* XIO_WRITE may grab the buffer it is writing if it was allocated by */
/* XIO_GETBUF. */
/* */
/* Read() hook for unpacking. */
static LONG xpkunpackin(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
LONG actuallen;
/* debug(("UnpackInHook: Type=%ld, Buf=%lx, Len=%ld.\n",type, *buf,size));*/
switch(type){
case XIO_READ:
if(!*buf){
if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
}
if( (actuallen = xRead(glob, fh->cfsfh.xfh, *buf, size)) != size){
return actuallen >= 0 ? XPKERR_TRUNCATED : XPKERR_IOERRIN;
}
break;
case XIO_WRITE:
debug(("Error: xpkunpackin(): Xpk attemps to XIO_WRITE.\n"));
return XPKERR_NOFUNC;
case XIO_FREE:
case XIO_ABORT:
FreeXpkInBuf(fh);
break;
case XIO_GETBUF:
if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
break;
case XIO_SEEK:
if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
return XPKERR_IOERROUT;
}
*buf = (APTR) 4L; /* VERY strange... */
break;
case XIO_TOTSIZE:
/* debug(("..... XIO_TOTSIZE: no action...\n")); */
break;
default:
debug(("*** PANIC: xpkunpackin(): Unknown type of action requested.\n"));
return XPKERR_NOFUNC;
}
return XPKERR_OK;
}
/* Write() hook for unpacking. */
static LONG xpkunpackout(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
/* debug(("UnpackOutHook: Type=%ld, Buf=%lx, Len=%ld.\n",type,*buf,size)); */
switch(type){
case XIO_READ:
debug(("Error: xpkunpackout(): Xpk attemps to XIO_READ.\n"));
return XPKERR_NOFUNC;
case XIO_WRITE:
if( *buf == fh->OutBuf /* && size == fh->OutBufLen */ ){
/* Writing our previously allocated buffer. Just add it to */
/* the filehandle, and mark it as used. */
/* NOTE: because of safety margin, the added memory block is */
/* larger than nesseeary. */
/* debug(("Adding previously allocated buffer to filehandle: %lx\n",*buf)); */
if(!addxpkmemchunk(fh->unpackedxpk,*buf,size)){
debug(("Error: xpkunpackout(): Cannot add data.\n"));
return XPKERR_NOMEM;
}
fh->OutBuf = NULL;
fh->OutBufLen = 0L;
}else{
UBYTE *newbuf;
/* debug(("Copying data into filehandle.\n")); */
if( !(newbuf = dosalloc(size)) ){
debug(("Error: xpkunpackout(): No memory.\n"));
return XPKERR_NOMEM;
}
CopyMem(*buf,newbuf,size);
if(!addxpkmemchunk(fh->unpackedxpk,newbuf,size)){
debug(("Error: xpkunpackout(): Cannot add data.\n"));
dosfree(newbuf);
return XPKERR_NOMEM;
}
}
break;
case XIO_FREE:
case XIO_ABORT:
FreeXpkOutBuf(fh);
break;
case XIO_GETBUF:
if( !(*buf = AllocXpkOutBuf(fh,size)) ){
debug(("Error: xpkunpackout(): Cannot get buffer for xpk.\n"));
return XPKERR_NOMEM;
}
/* debug(("xpkunpackout()/XIO_GETBUF: returning buffer %lx\n",*buf)); */
break;
case XIO_SEEK:
/* Strange... Surely this code is wrong? I mean, there's no file
* handle, is there? Commented out for now. */
/* if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){ */
/* return XPKERR_IOERROUT; */
/* } */
/* *buf = (APTR) 4L; /* VERY strange... */
debug(("Error: xpkunpackout(): Xpk attempts to XIO_SEEK.\n"));
return XPKERR_NOFUNC;
/* break; */
case XIO_TOTSIZE:
/* debug(("..... XIO_TOTSIZE: no action...\n")); */
break;
default:
debug(("*** PANIC: xpkunpackout(): Unknown type of action requested.\n"));
return XPKERR_NOFUNC;
}
return XPKERR_OK;
}
/* Hooks for Xpk packing.
* Currently, these use simple XpkFH file handles. However, I'm hoping
* to eventually make them use async I/O.
* NOTE BIEN: these XpkFH are FAKE, and cannot be used as such safely.
* Specifically, the cfsfh is not valid (though cfsfh.xfh is).
*/
/* NOTE: xpkpackin() / xpkpackout() are also used for unpacking. */
/* Read() hook for packing. */
static LONG xpkpackin(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
LONG actuallen;
/* debug(("PackInHook: Type=%ld, Buf=%lx, Len=%ld.\n",type, *buf,size));*/
switch(type){
case XIO_READ:
if(!*buf){
if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
}
if( (actuallen = xRead(glob, fh->cfsfh.xfh, *buf, size)) != size){
return actuallen >= 0 ? XPKERR_TRUNCATED : XPKERR_IOERRIN;
}
break;
case XIO_WRITE:
debug(("Error: xpkpackin(): Xpk attemps to XIO_WRITE.\n"));
return XPKERR_NOFUNC;
case XIO_FREE:
case XIO_ABORT:
FreeXpkInBuf(fh);
break;
case XIO_GETBUF:
if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
break;
case XIO_SEEK:
if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
return XPKERR_IOERROUT;
}
*buf = (APTR) 4L; /* VERY strange... */
break;
case XIO_TOTSIZE:
/* debug(("..... XIO_TOTSIZE: no action...\n")); */
break;
default:
debug(("*** PANIC: xpkpackin(): Unknown type of action requested.\n"));
return XPKERR_NOFUNC;
}
return XPKERR_OK;
}
/* Write() hook for packing. */
static LONG xpkpackout(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
/* debug(("PackOutHook: Type=%ld, Buf=%lx, Len=%ld.\n",type,*buf,size));*/
switch(type){
case XIO_READ:
debug(("Error: xpkpackout(): Xpk attemps to XIO_READ.\n"));
return XPKERR_NOFUNC;
case XIO_WRITE:
if(xWrite(glob, fh->cfsfh.xfh, *buf, size) != size){
return XPKERR_IOERROUT;
}
break;
case XIO_FREE:
case XIO_ABORT:
FreeXpkOutBuf(fh);
break;
case XIO_GETBUF:
if( !(*buf = AllocXpkOutBuf(fh,size)) ){
debug(("Error: xpkpackout(): Cannot get buffer for xpk.\n"));
return XPKERR_NOMEM;
}
/* debug(("xpkpackout()/XIO_GETBUF: returning buffer %lx\n",*buf)); */
break;
case XIO_SEEK:
if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
return XPKERR_IOERROUT;
}
*buf = (APTR) 4L; /* VERY strange... */
break;
case XIO_TOTSIZE:
/* debug(("..... XIO_TOTSIZE: no action...\n")); */
break;
default:
debug(("*** PANIC: xpkpackout(): Unknown type of action requested.\n"));
return XPKERR_NOFUNC;
}
return XPKERR_OK;
}
/*------------------------------------------------------------------------*
* Low-level interface to Xpk functions.
*/
/* The following code is there to support pre-2.0 OS versions. The
* problem is that Xpk insists on doing OpenLibrary() itself, which
* causes the dreaded ASYNCPKT guru.
*/
struct xpkunpackmsg{
struct Message msg;
void (*func)();
glb glob;
struct TagItem *tags;
LONG result;
};
struct xpkexammsg{
struct Message msg;
void (*func)();
glb glob;
struct XpkFib *fib;
struct TagItem *tags;
LONG result;
};
struct xpkpackmsg{
struct Message msg;
void (*func)();
glb glob;
struct TagItem *tags;
LONG result;
};
static void __asm CallXpkUnpackTags(register __a0 struct xpkunpackmsg *msg){
struct MsgPort *port;
debug(("Calling XpkUnpack(%lx)...", msg->tags ));
port = msg->glob->ioport;
if(msg->glob->ioport=CreatePort(NULL,0L)){
msg -> result = XpkUnpack(msg->tags);
DeletePort(msg->glob->ioport);
}else{
debug(("(no port)"));
msg->result = XPKERR_NOMEM;
}
msg->glob->ioport = port;
debug(("=%ld\n",msg->result));
}
static void __asm CallXpkExamine(register __a0 struct xpkexammsg *msg){
struct MsgPort *port;
debug(("Calling XpkExamine(%lx,%lx)...", msg->fib, msg->tags ));
port = msg->glob->ioport;
if(msg->glob->ioport=CreatePort(NULL,0L)){
msg -> result = XpkExamine(msg->fib, msg->tags);
DeletePort(msg->glob->ioport);
}else{
debug(("(no port)"));
msg->result = XPKERR_NOMEM;
}
msg->glob->ioport = port;
debug(("=%ld\n",msg->result));
}
static void __asm CallXpkPackTags(register __a0 struct xpkpackmsg *msg){
struct MsgPort *port;
debug(("Calling XpkPack(%lx)...", msg->tags ));
port = msg->glob->ioport;
if(msg->glob->ioport=CreatePort(NULL,0L)){
msg -> result = XpkPack(msg->tags);
DeletePort(msg->glob->ioport);
}else{
debug(("(no port)"));
msg->result = XPKERR_NOMEM;
}
msg->glob->ioport = port;
debug(("=%ld\n",msg->result));
}
static LONG MyXpkUnpackTags( glb glob, ULONG firsttag, ... ){
/* Cannot call xpk from a Task (or handler) before KS 2.0. */
if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
return XpkUnpack( (struct TagItem *)&firsttag );
}else{
struct xpkunpackmsg msg,*msg2;
extern void DoDOSSeg();
struct MsgPort *procid;
msg.msg.mn_Node.ln_Succ=NULL;
msg.msg.mn_Node.ln_Pred=NULL;
msg.msg.mn_Node.ln_Name=NULL;
msg.msg.mn_Node.ln_Type=NT_MESSAGE;
msg.msg.mn_Node.ln_Pri=0;
msg.msg.mn_ReplyPort=glob->xpkport;
msg.msg.mn_Length=sizeof(msg);
msg.func=(void (*)()) CallXpkUnpackTags;
msg.glob = glob;
msg.tags = (struct TagItem *)&firsttag;
if(!(procid=CreateProc /* Lets pray that 10K stack is enough... */
("XpkUnpack()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
return XPKERR_NOMEM;
PutMsg(procid,(struct Message *)&msg);
do WaitPort(glob->xpkport);
while(!(msg2=(struct xpkunpackmsg *)GetMsg(glob->xpkport)));
#ifdef DEBUG
if(msg2!=&msg)
KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
return msg2->result;
}
}
static LONG MyXpkExamine( glb glob, struct XpkFib *fib, ULONG firsttag, ... ){
/* Cannot call xpk from a Task (or handler) before KS 2.0. */
if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
return XpkExamine( fib, (struct TagItem *)&firsttag );
}else{
struct xpkexammsg msg,*msg2;
extern void DoDOSSeg();
struct MsgPort *procid;
msg.msg.mn_Node.ln_Succ=NULL;
msg.msg.mn_Node.ln_Pred=NULL;
msg.msg.mn_Node.ln_Name=NULL;
msg.msg.mn_Node.ln_Type=NT_MESSAGE;
msg.msg.mn_Node.ln_Pri=0;
msg.msg.mn_ReplyPort=glob->xpkport;
msg.msg.mn_Length=sizeof(msg);
msg.func=(void (*)()) CallXpkExamine;
msg.glob = glob;
msg.fib = fib;
msg.tags = (struct TagItem *)&firsttag;
if(!(procid=CreateProc /* Lets pray that 10K stack is enough... */
("XpkExamine()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
return XPKERR_NOMEM;
PutMsg(procid,(struct Message *)&msg);
do WaitPort(glob->xpkport);
while(!(msg2=(struct xpkexammsg *)GetMsg(glob->xpkport)));
#ifdef DEBUG
if(msg2!=&msg)
KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
return msg2->result;
}
}
static LONG MyXpkPackTags( glb glob, ULONG firsttag, ... ){
/* Cannot call Xpk from a Task (or handler) before KS 2.0. */
if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
return XpkPack( (struct TagItem *)&firsttag );
}else{
struct xpkpackmsg msg,*msg2;
extern void DoDOSSeg();
struct MsgPort *procid;
msg.msg.mn_Node.ln_Succ=NULL;
msg.msg.mn_Node.ln_Pred=NULL;
msg.msg.mn_Node.ln_Name=NULL;
msg.msg.mn_Node.ln_Type=NT_MESSAGE;
msg.msg.mn_Node.ln_Pri=0;
msg.msg.mn_ReplyPort=glob->xpkport;
msg.msg.mn_Length=sizeof(msg);
msg.func=(void (*)())CallXpkPackTags;
msg.glob = glob;
msg.tags = (struct TagItem *)&firsttag;
if(!(procid=CreateProc /* Lets pray that 10K stack is enough... */
("XpkPack()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
return XPKERR_NOMEM;
PutMsg(procid,(struct Message *)&msg);
do WaitPort(glob->xpkport);
while(!(msg2=(struct xpkpackmsg *)GetMsg(glob->xpkport)));
#ifdef DEBUG
if(msg2!=&msg)
KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
return msg2->result;
}
}
/* This function opens an existing file in the xpk format, unpacking
* it to memory with the help of the xpk.library. Note that due to
* limitation in the current interface of xpk.library, the whole file
* must be unpacked at once, which is more speed efficient (and by far
* the easiest to implement), but requires rather a lot of memory
* (potentially requiring the data to reside 2-3 times in main memory
* simultaneously) and could result in memory fragmentation.
*/
struct XpkFH *XpkOpenOldFile( glb glob, struct FileHandle *xfh ){
struct XpkFH *fh;
LONG res;
LONG inlen;
struct Hook inhook,outhook;
struct xpkhookdata indata,outdata;
if(!dalloc(fh)){
OUTOFMEM;
return NULL;
}
fh->cfsfh.objtype = XPKOBJECT;
fh->cfsfh.mode = MODE_OLDFILE;
fh->cfsfh.xfh = xfh;
fh->cfsfh.f = &Xpkfunc;
/* fh->cfsfh.filename and fh->cfsfh.parent NULL by default. */
if(!(fh->unpackedxpk=newunpackedxpk())){
OUTOFMEM;
dfree(fh);
return NULL;
}
inhook.h_Entry = (ULONG (*)())DoXpkHook;
inhook.h_SubEntry = (ULONG (*)())xpkunpackin;
indata.glob=glob;
indata.fh=fh;
inhook.h_Data = &indata;
outhook.h_Entry = (ULONG (*)())DoXpkHook;
outhook.h_SubEntry = (ULONG (*)())xpkunpackout;
outdata.glob=glob;
outdata.fh=fh;
outhook.h_Data = &outdata;
/* Get length of file, if possible. */
inlen = xFileSizeXfh( glob, xfh );
glob->ioerr = 0L; /* Set by hookfuncs if adequate. */
res = MyXpkUnpackTags( glob,
XPK_InHook, &inhook,
XPK_OutHook, &outhook,
XPK_Password, glob->xpkpassword,
TAGIF(inlen!=-1L,XPK_InLen), inlen,
TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
TAG_DONE
);
debug(("XpkUnPack() returned: %ld\n",res));
FreeXpkInBuf(fh);
FreeXpkOutBuf(fh);
if(res != XPKERR_OK){
if(!glob->ioerr){
/* ToDo: Since no adequate AmigaDOS error code exists, we */
/* should perhaps open a requester to inform the user of */
/* the problem (showing an XpkErr)? */
glob->ioerr = ERROR_OBJECT_WRONG_TYPE; /* More likely out of memory. */
}
killunpackedxpk(fh->unpackedxpk);
dfree(fh);
return NULL;
}
if(fh->unpackedxpk->first)
fh->filelen = fh->unpackedxpk->last->abspos
+ fh->unpackedxpk->last->size; /* else 0 by default. */
return fh;
}
/* Perform a XpkExamine() on an UFS file handle. */
BOOL XpkExamine_FH( glb glob, struct FileHandle *xfh, struct XpkFib *fib ){
LONG res;
LONG inlen;
struct XpkFH *fh;
struct Hook inhook;
struct xpkhookdata indata;
if(!dalloc(fh)){ /* NOTE: NOT a real xpkfilehandle (only InBuf is used). */
OUTOFMEM;
return FALSE;
}
fh->cfsfh.xfh = xfh;
inhook.h_Entry = (ULONG (*)())DoXpkHook;
inhook.h_SubEntry = (ULONG (*)())xpkunpackin;
indata.glob=glob;
indata.fh=fh;
inhook.h_Data = &indata;
/* Get length of file, if possible. */
inlen = xFileSizeXfh( glob, xfh );
glob->ioerr = 0L; /* Set by hookfuncs if adequate. */
res = MyXpkExamine( glob, fib,
XPK_InHook,&inhook,
TAGIF(inlen!=-1L,XPK_InLen), inlen,
TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
TAG_DONE
);
debug(("XpkExamine() returned: %ld\n",res));
FreeXpkInBuf(fh);
dfree(fh);
/* Need a better error message here. */
if( res && !glob->ioerr ) glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
return (BOOL)( res==XPKERR_OK );
}
/* This is the function that does the actual compression (using xpk) from
* one file handle to another.
*/
static BOOL XpkPackFH2FH(glb glob, struct FileHandle *srcxfh,
LONG inlen, struct FileHandle *dstxfh){
struct XpkFH *srcxpkfh,*dstxpkfh; /* NOTE: these are FAKE/invalid. */
LONG res;
LONG outlen; /* Dummy variable. */
struct Hook inhook,outhook;
struct xpkhookdata indata,outdata;
/* Create the two fake XpkFH filehandles for the hooks. */
if(!dalloc(srcxpkfh)){
OUTOFMEM;
return FALSE;
}
if(!dalloc(dstxpkfh)){
OUTOFMEM;
dfree(srcxpkfh);
return FALSE;
}
srcxpkfh->cfsfh.xfh = srcxfh; /* Buffer pointers NULL by default. */
dstxpkfh->cfsfh.xfh = dstxfh; /* Buffer pointers NULL by default. */
/* Set up hooks. */
inhook.h_Entry = (ULONG (*)())DoXpkHook;
inhook.h_SubEntry = (ULONG (*)())xpkpackin;
indata.glob=glob;
indata.fh=srcxpkfh;
inhook.h_Data = &indata;
outhook.h_Entry = (ULONG (*)())DoXpkHook;
outhook.h_SubEntry = (ULONG (*)())xpkpackout;
outdata.glob=glob;
outdata.fh=dstxpkfh;
outhook.h_Data = &outdata;
glob->ioerr = 0L; /* Set by hookfuncs if adequate. */
res = MyXpkPackTags( glob,
XPK_InHook,&inhook,
XPK_OutHook,&outhook,
XPK_InLen,inlen,
XPK_PackMethod,glob->packmode,
XPK_StepDown,glob->stepdown,
XPK_Password,glob->xpkpassword,
XPK_GetOutLen,&outlen, /* NOTE: not used, buf xpk.doc says needed. */
TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
TAG_DONE
);
debug(("XpkPack() returned: %ld\n",res));
FreeXpkInBuf(srcxpkfh);
FreeXpkOutBuf(dstxpkfh);
dfree(srcxpkfh);
dfree(dstxpkfh);
if(res != XPKERR_OK){
if(!glob->ioerr){
/* ToDo: Since no adequate AmigaDOS error code exists, we */
/* should perhaps open a requester to inform the user of */
/* the problem (showing an XpkErr)? */
glob->ioerr = ERROR_OBJECT_WRONG_TYPE; /* More likely out of memory. */
}
return FALSE;
}
return TRUE;
}
/* This is the function that does the actual compression (using xpk) from
* one file handle to another.
*/
static BOOL XpkUnPackFH2FH(glb glob, struct FileHandle *srcxfh,
LONG inlen, struct FileHandle *dstxfh){
struct XpkFH *srcxpkfh,*dstxpkfh; /* NOTE: these are FAKE/invalid. */
LONG res;
struct Hook inhook,outhook;
struct xpkhookdata indata,outdata;
/* Create the two fake XpkFH filehandles for the hooks. */
if(!dalloc(srcxpkfh)){
OUTOFMEM;
return FALSE;
}
if(!dalloc(dstxpkfh)){
OUTOFMEM;
dfree(srcxpkfh);
return FALSE;
}
srcxpkfh->cfsfh.xfh = srcxfh; /* Buffer pointers NULL by default. */
dstxpkfh->cfsfh.xfh = dstxfh; /* Buffer pointers NULL by default. */
/* Set up hooks. */
inhook.h_Entry = (ULONG (*)())DoXpkHook;
inhook.h_SubEntry = (ULONG (*)())xpkpackin;
indata.glob=glob;
indata.fh=srcxpkfh;
inhook.h_Data = &indata;
outhook.h_Entry = (ULONG (*)())DoXpkHook;
outhook.h_SubEntry = (ULONG (*)())xpkpackout;
outdata.glob=glob;
outdata.fh=dstxpkfh;
outhook.h_Data = &outdata;
glob->ioerr = 0L; /* Set by hookfuncs if adequate. */
res = MyXpkUnpackTags( glob,
XPK_InHook,&inhook,
XPK_OutHook,&outhook,
XPK_InLen,inlen,
XPK_Password,glob->xpkpassword,
TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
TAG_DONE
);
debug(("XpkUnPack() returned: %ld\n",res));
FreeXpkInBuf(srcxpkfh);
FreeXpkOutBuf(dstxpkfh);
dfree(srcxpkfh);
dfree(dstxpkfh);
if(res != XPKERR_OK){
if(!glob->ioerr){
/* ToDo: Since no adequate AmigaDOS error code exists, we */
/* should perhaps open a requester to inform the user of */
/* the problem (showing an XpkErr)? */
glob->ioerr = ERROR_OBJECT_WRONG_TYPE; /* More likely out of memory. */
}
return FALSE;
}
return TRUE;
}
/* NOTE BIEN: This function preserves the lock! */
struct XpkFH *XpkOpenOldFileFromCopyOfLock( glb glob, struct XpkLock *lock ){
struct FileHandle *xfh;
struct XpkFH *fh;
if(!(xfh = xOpenFromCopyOfLock(glob, lock->cfslock.xlock))){
debug(("Unable to xOpenFromLock(): %ld.\n",glob->ioerr));
return NULL;
}
fh = XpkOpenOldFile( glob, xfh );
if(!fh){
LONG saveioerr = glob->ioerr;
xClose(glob,xfh);
glob->ioerr = saveioerr;
}
return fh;
}
/* Check if a given filehandle belongs to an Xpk file. glob->ioerr
* set and FALSE returned in case of error.
*/
BOOL IsXpkFile( glb glob, struct FileHandle *xfh ){
struct XpkFib fib;
if( !XpkExamine_FH(glob, xfh, &fib) ){
debug(("Error: IsXpkFile: XpkExamine_FH returned error\n"));
return FALSE;
}else if( fib.Type == XPKTYPE_PACKED ){
return TRUE;
}else{
glob->ioerr = 0L;
return FALSE;
}
}
/* This is the main entry to the automatic compression, and is the
* function passed to TransFormFile().
*/
BOOL PackFile2File(glb glob, struct FileHandle *srcxfh,
struct FileHandle *dstxfh, void *dummy){
LONG srcsize;
srcsize = xGetFileSize(glob, srcxfh);
if(srcsize == -1L){
debug(("Error: PackFile2File(): Could not obtain file length.\n"));
return FALSE;
}
return XpkPackFH2FH(glob, srcxfh, srcsize, dstxfh);
}
/* This function is passed to TransFormFile() to unpack a file to another
* file (to handle Write() to an Xpk file).
*/
BOOL UnPackFile2File(glb glob, struct FileHandle *srcxfh,
struct FileHandle *dstxfh, void *dummy){
LONG srcsize;
srcsize = xGetFileSize(glob, srcxfh);
if(srcsize == -1L){
debug(("Error: UnPackFile2File(): Could not obtain file length.\n"));
return FALSE;
}
return XpkUnPackFH2FH(glob, srcxfh, srcsize, dstxfh);
}
/*------------------------------------------------------------------------*
* Virtual functions for Open(), Read(), Lock() etc.
*/
LONG Xpk_Read( glb glob, struct XpkFH *fh, UBYTE *buf, LONG len ){
if( fh->unpackedxpk->currentpos+len > fh->filelen )
len = fh->filelen - fh->unpackedxpk->currentpos;
if( len <= 0 ) return 0L;
if(!readunpackedxpk(fh->unpackedxpk, buf, len )){
return -1L;
}
return len;
}
LONG Xpk_Seek( glb glob, struct XpkFH *fh, LONG pos, LONG offset ){
LONG abspos;
LONG oldpos = fh->unpackedxpk->currentpos;
if( offset != OFFSET_BEGINNING &&
offset != OFFSET_CURRENT &&
offset != OFFSET_END ){
debug(("Error: Xpk: Bad offset value for Seek(): %ld\n",offset));
glob->ioerr = ERROR_ACTION_NOT_KNOWN;
return -1L;
}
abspos = abs_seek_pos( fh->unpackedxpk->currentpos, fh->filelen, pos, offset );
if( abspos < 0 || abspos > fh->filelen ){
debug(("Error: Xpk: Bad abs. pos. in Seek(): %ld\n",abspos));
glob->ioerr = ERROR_SEEK_ERROR;
return -1L;
}
debug(("XpkSeek(): fh=%lx, pos=%ld, offset=%ld, abspos=%ld\n",fh,pos,offset,abspos));
seekunpackedxpk(fh->unpackedxpk,abspos);
return oldpos;
}
BOOL Xpk_Close( glb glob, struct XpkFH *fh ){
BOOL res;
if(!fh->cfsfh.xfh){
debug(("Xpk_Close(): Bad fh.\n"));
glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
return FALSE;
}
res = xClose( glob, fh->cfsfh.xfh );
if( res ){
killunpackedxpk(fh->unpackedxpk);
XObjUnStuffFH(glob, &fh->cfsfh);
dfree(fh);
}else{
debug(("ERROR: Xpk: xClose() returned false - mem not freed.\n"));
}
return res;
}
/* Xpk_Write() needs to do some magic to prepare the file for writing. The
* idea is to replace the file (on disk) with the unpacked data, then make
* the file handle look like a CFSFH.
*
* One possibility would be to write the data in memory. However, since
* the file could have changed since this Open(), it's safer to
* uncompress the file again.
*/
LONG Xpk_Write(glb glob, struct XpkFH *fh, UBYTE *buf, LONG len){
BOOL res;
char *filename = fh->cfsfh.filename;
struct FileLock *xlock = fh->cfsfh.parent->xlock;
struct FileHandle *xfh = fh->cfsfh.xfh;
LONG currentpos;
if(!glob->allowappend){
debug(("Xpk_Write(): Appendmode not set - failing.\n"));
glob->ioerr = ERROR_ACTION_NOT_KNOWN;
return -1L;
}
if(!xfh){
debug(("XpkWrite(): Bad fh.\n"));
glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
return -1L;
}
if(!filename){
debug(("Error: Xpk_Write(): no name info.\n"));
glob->ioerr = ERROR_ACTION_NOT_KNOWN;
return -1L;
}
debug(("Attempting to uncompress file %s.\n",filename));
if(!xChangeMode(glob, CHANGE_FH, xfh, MODE_NEWFILE)){
debug(("Error: Xpk_Write(): xChangeMode(): %ld.\n",glob->ioerr));
return -1L;
}
if(-1L == xSeek(glob, xfh, 0L, OFFSET_BEGINNING)){
debug(("Error: Xpk_Write(): Could not seek to start: %ld.\n",glob->ioerr));
return -1L;
}
res = TransformXFH(glob, xfh, xlock, filename, UnPackFile2File, NULL);
debug(("TransformFile() returned: %ld.\n",res));
fh->cfsfh.xfh = xfh = NULL; /* The handle was closed by TransformFile(). */
if(!res){
SAVEIOERR;
/* Bad luck! The uncompress didn't work. Since our xfh is gone, we'll
* have to do some magic to save as much as possible.
*/
if( !(xfh=xOpen(glob, xlock, filename, fh->cfsfh.mode)) ){
debug(("Error: File %s could not be re-opened: %d.\n",filename,glob->ioerr));
/* Nothing to do about it, really... */
}
RESTIOERR;
return -1L;
}
if( !(xfh=xOpen(glob, xlock, filename, fh->cfsfh.mode)) ){
debug(("Error: File %s could not be re-opened: %d.\n",filename,glob->ioerr));
/* Nothing to do about it, really... */
return -1L;
}
fh->cfsfh.xfh = xfh;
currentpos = fh->unpackedxpk->currentpos;
killunpackedxpk(fh->unpackedxpk);
if(!glob->compressreadwrite) XObjUnStuffFH(glob, &fh->cfsfh);
XObjStealXpkFH(glob, &fh->cfsfh);
if(-1L == xSeek(glob, xfh, currentpos, OFFSET_BEGINNING)){
debug(("Error: Xpk_Write(): Could not seek to right pos: %ld.\n",glob->ioerr));
return -1L;
}else{
return XObjWrite(glob, &fh->cfsfh, buf, len);
}
}
struct XpkLock *XpkMakeLock( glb glob, struct FileLock *xlock, LONG mode ){
struct XpkLock *newlock;
if( !dalloc(newlock) ){
OUTOFMEM;
return NULL;
}
newlock->cfslock.objtype = XPKOBJECT;
newlock->cfslock.mode = mode;
newlock->cfslock.xlock = xlock;
newlock->cfslock.f = &Xpkfunc;
newlock->cfslock.refcount = 0; /* NOTE: NOT used for XpkObject's. */
return newlock;
}
struct XpkLock *XpkDupLock( glb glob, struct XpkLock *lock ){
struct XpkLock *newlock;
/* XPKOBJECT locks have their xLocks xDupLock'ed, and the rest of
* the fields are just copied vanilla.
*/
if( !dalloc(newlock) ){
OUTOFMEM;
return 0L;
}
*newlock = *lock;
newlock->cfslock.refcount = 0L; /* Again: refcount is not used. */
if( !(newlock->cfslock.xlock = xDupLock(glob, lock->cfslock.xlock)) ){
dfree(newlock);
return 0L;
}
return newlock;
}
/* NOTE BIEN: The parent lock is a XOBJECT, not a XPKOBJECT! */
struct CFSLock *XpkParentDir( glb glob, struct XpkLock *lock ){
struct FileLock *xlock;
struct CFSLock *newlock;
if( !(xlock = xParentDir(glob, lock->cfslock.xlock)) ){
return 0L;
}
newlock = XObjMakeLock( glob, xlock, ACCESS_READ );
if( !newlock ){
LONG saveioerr = glob->ioerr;
xUnLock(glob, xlock);
glob->ioerr = saveioerr;
}
return newlock;
}
/* NOTE BIEN: The parent lock is a XOBJECT, not a XPKOBJECT! */
struct CFSLock *XpkParentFH( glb glob, struct XpkFH *fh ){
return XObjParentFH(glob, &fh->cfsfh);
}
BOOL XpkUnLock( glb glob, struct XpkLock *lock ){
BOOL err;
if(lock->cfslock.refcount){
/* This should NOT have happened! */
debug(("\n\n***PANIC***: XpkUnLock: Nonzero refcount in lock (%ld).\n",lock->cfslock.refcount));
return DOSFALSE;
}
err = xUnLock(glob, lock->cfslock.xlock);
/* Now, what if the UnLock fails? I guess it's best to return FALSE
* and let the lock live.
*/
if(!err) return DOSFALSE;
dfree(lock);
return DOSTRUE;
}
BOOL XpkSameLock(glb glob, struct XpkLock *l1, struct XpkLock *l2 ){
return (BOOL) (xSameLock( glob, l1->cfslock.xlock, l2->cfslock.xlock ) == LOCK_SAME);
}
LONG ATUL ( char *Ptr ){
UWORD Index;
LONG LW;
for (Index=0,LW=0L;Index<8;Index++)
LW=(LW<<4)+(LONG)(*Ptr++-'A');
return LW;
}
BOOL XpkFastModifyFIB( struct FileInfoBlock *FIB ){
if( (strncmp( FIB->fib_Comment, XFH_ID, 5 ) != 0) ||
(strlen( FIB->fib_Comment ) != 50) ) return FALSE;
debug(("XpkFastModifyFIB: header found: %s\n",FIB->fib_Comment));
FIB->fib_Comment[0] = '\0';
if ( (ATUL( &FIB->fib_Comment[6] ) == FIB->fib_Date.ds_Days) &&
(ATUL( &FIB->fib_Comment[15] ) == FIB->fib_Date.ds_Minute) &&
(ATUL( &FIB->fib_Comment[24] ) == FIB->fib_Date.ds_Tick) &&
(ATUL( &FIB->fib_Comment[33] ) == FIB->fib_Size) ){
FIB->fib_Size = ATUL( &FIB->fib_Comment[42] );
debug(("XpkFastModifyFIB: header valid, size: %ld\n",FIB->fib_Size));
return TRUE;
}
return FALSE;
}
BOOL XpkObjExamine( glb glob, struct XpkLock *lock, struct FileInfoBlock *fib ){
BOOL err;
struct FileHandle *xfh;
debug(("XpkObjExamine(): "));
err = xExamine(glob, lock->cfslock.xlock, fib);
debug(("%ld,'%s'\n",err,fib->fib_FileName));
if( !err ) return err;
if( XpkFastModifyFIB( fib ) ) return DOSTRUE;
xfh = xOpenFromCopyOfLock( glob, lock->cfslock.xlock );
if( !xfh ){
debug(("Error: XpkExamine(): Cannot open file from lock %lx.\n",lock));
return DOSFALSE;
}
err = XpkModifyFIB( glob, lock, fib, xfh );
if( !err ){
LONG saveioerr;
saveioerr = glob->ioerr;
xClose( glob, xfh );
glob->ioerr = saveioerr;
return DOSFALSE;
}else{
xClose( glob, xfh );
return DOSTRUE;
}
}
BOOL XpkObjExamineFH( glb glob, struct XpkFH *fh, struct FileInfoBlock *fib ){
BOOL err;
debug(("XpkObjExamineFH(): "));
err = xExamineFH(glob, fh->cfsfh.xfh, fib);
debug(("%ld,'%s'\n",err,fib->fib_FileName));
if( !err ) return err;
if( (strncmp( fib->fib_Comment, XFH_ID, 5 ) == 0) &&
(strlen( fib->fib_Comment ) == 50) ) fib->fib_Comment[0] = '\0';
fib->fib_Size = fh->filelen;
return DOSTRUE;
}
/* XpkModifyFIB() has to fix the length of the file. */
BOOL XpkModifyFIB( glb glob, struct XpkLock *lock,struct FileInfoBlock *fib, struct FileHandle *xfh ){
struct XpkFib xpkfib;
if( !XpkExamine_FH(glob, xfh, &xpkfib) ){
debug(("Error: IsXpkFile: XpkExamine_FH returned error\n"));
return FALSE;
}
fib->fib_Size = xpkfib.ULen;
/* ToDo: Should NOT have fixed block size (look at UFS). */
fib->fib_NumBlocks = (fib->fib_Size+BLOCKSIZE-1) / BLOCKSIZE;
return DOSTRUE;
}
/*------------------------------------------------------------------------*
* Initialise and cleanup functions.
*/
BOOL InitXpk( glb glob ){
if(!(glob->XpkBase = OpenLibrary("xpkmaster.library",0L)))
return FALSE;
XpkBase = glob->XpkBase;
if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
glob->xpkport = NULL;
}else{
if(!(glob->xpkport=CreatePort("CFS port for KS1.3 Xpk-calls.",0L))){
CloseLibrary(glob->XpkBase);
debug(("Error creating xpk message port.\n"));
return FALSE;
}
}
return TRUE;
}
void CleanupXpk( glb glob ){
if( glob->xpkport ) DeletePort( glob->xpkport );
if( glob->XpkBase ) CloseLibrary( XpkBase );
}
/* xScan support */
static char *ULTA(char *Ptr,ULONG LW)
{
UWORD Index;
*Ptr++=' ';
for (Index=0; Index<8; Index++, LW>>=4) Ptr[7-Index]='A'+(char)(LW&15);
return &Ptr[8];
}
void xScan(glb glob,struct FileLock *ParentLock,char *Name)
{
struct FileLock *xScanLock;
struct FileHandle *xScanHandle;
struct XpkFib XpkFib;
if (!glob->xscan) return;
debug (("xScan(%lx,%s)\n",ParentLock,Name));
if (xScanLock=xLock(glob,ParentLock,Name,ACCESS_READ))
{
if (xExamine(glob,xScanLock,&glob->fib2))
if (xScanHandle=xOpen(glob,ParentLock,Name,MODE_OLDFILE))
{
if (XpkExamine_FH(glob,xScanHandle,&XpkFib))
if ((glob->fib2.fib_Comment[0]=='\0')||
(strncmp(glob->fib2.fib_Comment,XFH_ID,strlen(XFH_ID))==0))
{
char *Ptr;
Ptr=strcpy(glob->fib2.fib_Comment,XFH_ID)+strlen(XFH_ID);
Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Days);
Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Minute);
Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Tick);
Ptr=ULTA(Ptr,glob->fib2.fib_Size);
*ULTA(Ptr,(XpkFib.Type==XPKTYPE_PACKED)?XpkFib.ULen:glob->fib2.fib_Size)='\0';
if (!xSetComment(glob,ParentLock,Name,glob->fib2.fib_Comment))
debug (("failed to set new comment: %ld\n",glob->ioerr));
}
else debug (("Comment in use -- nothing done\n"));
else debug (("failed to XpkExamine\n"));
(void)xClose(glob,xScanHandle);
}
else debug (("failed to open file\n"));
else debug (("failed to examine\n"));
(void)xUnLock(glob,xScanLock);
}
else debug (("failed to open file\n"));
}
/* End of xpk.c */